﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Web;
using System.IO;

namespace Management
{
    public class Dao
    {
        //@"C:\Users\Michael13\Documents\Visual Studio 2013\Projects\CourseManagement\Management\Descriptions\";
        private const string defaultProgress = "In progress";
        private const string endProgress = "Ready";
        private const string descriptionPath = @"C:\Users\Michael13\Documents\Visual Studio 2013\Projects\CourseManagement\Management\Descriptions\";
        private DBFrameworkDataContext context;
        public readonly string[] weekDays = {
                                "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
                             };
        /*public readonly string[] weekDays = {
                                "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"
                             };*/
        private readonly DateTime startDate = Convert.ToDateTime("04.01.2015");

        public Dao()
        {
            context = new DBFrameworkDataContext(ConfigurationManager.ConnectionStrings["DBConnectionString"].ConnectionString);
        }

        public List<Goal> GetAllGoals()
        {
            return context.Goals.ToList();
        }

        public Goal GetGoal(int goal)
        {
            return (from goals in context.Goals
                where goals.Id == goal
                select goals).FirstOrDefault();
        }

        public Task GetTask(int task, int goal)
        {
            return (from tasks in context.Tasks
                where tasks.goal == goal
                      && tasks.Id == task
                select tasks).FirstOrDefault();
        }

        public List<Goal> GetGoalsWithExistingTime(int weekNumber)
        {
            return context.ExecuteQuery<Goal>(
                "SELECT * FROM Goals WHERE Id = ANY(SELECT goal FROM Tasks WHERE Id = ANY(SELECT task FROM Weeks WHERE time IS NOT NULL AND number={0}))", weekNumber)
                .ToList();
        }

        public List<Task> GetAllTasks(int goal)
        {
            return (
                from task in context.Tasks
                where task.goal == goal
                select task
                ).ToList();
        }

        public string GetTaskDescription(int goal, Task task)
        {
            try
            {
                return GetFileData(GetPath(GetDescriptionName(goal, task.name)));
            }
            catch (Exception)
            {

                return "Exception!!!";
            }
        }

        private string GetFileData(string path)
        {
            var sr = new StreamReader(path);
            string line, result="";
            while ((line=sr.ReadLine())!=null)
            {
                result += line;
            }
            sr.Close();
            return result;
        }
        private string GetPath(string descriptionName)
        {
            return descriptionPath + descriptionName;
        }
        private string GetDescriptionName(int goalId, string taskName)
        {
            return goalId + ReplaceSpace(taskName) + ".txt";
        }
        private DateTime GetLastWeekDate()
        {
            if (!context.Weeks.Any())
                return startDate;
            return
                (
                    from week in context.Weeks
                    select week.date
                ).Max();
        }

        public int GetLastWeek()
        {
            if (!context.Weeks.Any())
                return 0;
            return
                (
                    from week in context.Weeks
                    select week.number
                ).Max();
        }

        public DateTime GetDateByWeekNumberAndDay(int number, string day)
        {
            return (from week in context.Weeks
                    where week.day == day &&
                          week.number == number
                    select week.date).FirstOrDefault();
        }
        public string GetMinWeekDate(int weekNumber)
        {
            var date = (
                from week in context.Weeks
                where week.number == weekNumber
                select week.date
                ).Min();
            return date.ToShortDateString();
        }

        public string GetMaxWeekDate(int weekNumber)
        {
            var date = (
                from week in context.Weeks
                where week.number == weekNumber
                select week.date
                ).Max();
            return date.ToShortDateString();
        }

        public List<Week> GetWeekDataByNumberAndTask(int weekNumber, Task task)
        {
            return (
                from week in context.Weeks
                where week.number == weekNumber
                      && week.task == task.Id
                select week
                ).ToList();
        }

        public string GetTimeByWeekTaskDay(int week, int task, string day)
        {
            return (from weeks in context.Weeks
                where weeks.number == week &&
                      weeks.task == task &&
                      weeks.day == day
                select weeks.time).FirstOrDefault();
        }

        private string ReplaceSpace(string replaceString)
        {
            return replaceString.Replace(' ', '~');
        }

        private void RemoveDescription(string path)
        {
            File.Delete(path);
        }
        private void SaveDescriptionToFile(string description, string path)
        {
            if (!File.Exists(path))
            {
                File.Create(path).Close();
            }
            var sw = new StreamWriter(path);
            try
            {
                sw.Write(description);
            }
            catch (Exception)
            {
                throw new Exception("Can't save description in the file!!!");
            }
            finally
            {
                sw.Close();
            }
        }

        public void AddGoal(string goal)
        {
            if (context.Goals.FirstOrDefault(p => p.Name == goal) == null)
            {

                var newGoal = new Goal
                {
                    Name = goal,
                };
                context.Goals.InsertOnSubmit(newGoal);
                try
                {
                    context.SubmitChanges();
                }
                catch (Exception)
                {
                    throw new Exception("Exception!!! Can't add new Goal");
                }
            }
            else
            {
                throw new Exception("Exception!!! Goal with such name already exists!");
            }
        }

        public Goal FindGoal(string goalName)
        {
            return context.Goals.FirstOrDefault(p => p.Name == goalName);
        }

        public void RemoveGoal(string goalName)
        {
            var goalToDelete = FindGoal(goalName);

            if (goalToDelete != null)
            {
                var tasks = goalToDelete.Tasks.ToArray();
                for (var i = 0; i < tasks.Length; i++)
                {
                    RemoveTask(tasks[i].name, goalName);
                }
                context.Goals.DeleteOnSubmit(goalToDelete);
                try
                {
                    context.SubmitChanges();
                }
                catch (Exception)
                {
                    throw new Exception("Can't remove goal "+goalName);
                }
            }
            else
            {
                throw new Exception("There is no such goal: " + goalName);
            }

        }

        public void AddTask(string goalName, string taskName, string description)
        {
            var goal = FindGoal(goalName);
            if (goal != null)
            {
                var oldTask = (
                    from task in context.Tasks
                    where task.name == taskName
                          && task.goal == goal.Id
                    select goal).FirstOrDefault();
                if (oldTask == null)
                {
                    var descriptioName = GetDescriptionName(goal.Id, taskName);
                    SaveDescriptionToFile(description, GetPath(descriptioName));
                    var newTask = new Task
                    {
                        goal = goal.Id,
                        name = taskName,
                        status = defaultProgress,
                        Description = descriptioName
                    };
                    context.Tasks.InsertOnSubmit(newTask);
                    try
                    {
                        context.SubmitChanges();
                        GenerateWeeks(newTask);
                    }
                    catch (Exception)
                    {
                        throw new Exception("Exception!!! Can't add new task");
                    }
                }
                else
                {
                    throw new Exception("Exception!!! Such task is already exist!");
                }
            }
            else
            {
                throw new Exception("Exception!!! Such goals does not exist!");
            }
        }


        public void RemoveTask(string taskName, string goalName)
        {
            var goal = FindGoal(goalName);
            if (goal != null)
            {
                var taskToDelete =
                    (
                        from task in context.Tasks
                        where task.name == taskName
                        && task.goal == goal.Id
                        select task).FirstOrDefault();
                if (taskToDelete != null)
                {
                    try
                    {
                        RemoveDescription(GetPath(taskToDelete.Description));
                        context.Tasks.DeleteOnSubmit(taskToDelete);
                        context.SubmitChanges();
                    }
                    catch (Exception)
                    {
                        throw new Exception("Can't remove task " + taskToDelete.name + "for goal" + goal.Name);
                    }
                    
                }
            }
        }
        public void ChangeTaskStatus(string goalName, string taskName)
        {
            var currentGoal = FindGoal(goalName);
            var currentTask = (
                from task in context.Tasks
                where currentGoal.Id == task.goal &&
                      task.name == taskName
                select task).FirstOrDefault();

            if (currentTask == null)
                throw new Exception("Exception! There is no such task!!!");

            if (currentTask.status.Equals(defaultProgress))
                currentTask.status = endProgress;

            else if (currentTask.status.Equals(endProgress))
                currentTask.status = defaultProgress;

            try
            {
                context.SubmitChanges();
            }
            catch (Exception)
            {
                throw new Exception("Can't change status for the task " + currentTask.name + "!!!");
            }
        }

        public void AddWeek(int weekNumber)
        {
            var lastWeek = GetLastWeek();
            var lastWeekDate = GetLastWeekDate();
            var tasks = context.Tasks.ToList();
                
            if(weekNumber<= lastWeek || weekNumber<=0)
            {
                throw new Exception("Current week already exists!");
            }
            for (int j = lastWeek + 1, k = 0; j <= weekNumber; j++,k++)
            {
                foreach (var task in tasks)
                {
                    for (var i = 0; i < weekDays.Length; i++)
                    {
                        InsertNewWeek(
                            new DateTime(lastWeekDate.Year, lastWeekDate.Month,
                                lastWeekDate.Day).AddDays(i + k*7 + 1), weekDays[i], j, task);
                    }
                }
            }
        }

        private void InsertNewWeek(DateTime date, string dayOfWeek, int weekNumber, Task task)
        {
            var newWeek = new Week
            {
                date = date.Date,
                day = dayOfWeek,
                number = weekNumber,
                task = task.Id
            };
            context.Weeks.InsertOnSubmit(newWeek);
            try
            {
                context.SubmitChanges();
            }
            catch (Exception)
            {
                throw new Exception("Exception!!! Can't add new week for task " + task.name);
            }
        }
 
        private void GenerateWeeks(Task task)
        {
            var lastWeek = GetLastWeek();
            for (var j = 0; j < lastWeek; j++)
            {
                for (var i = 0; i < weekDays.Length; i++)
                {
                    InsertNewWeek(new DateTime(startDate.Year, startDate.Month,
                        startDate.Day).AddDays(i + j*7 + 1), weekDays[i], j+1, task);
                }
            }
        }

        public void SaveWeekTimeInfo(string day, int week, int task, string time)
        {
            var weekRow = (from weeks in context.Weeks
                where weeks.day == day &&
                      weeks.number == week &&
                      weeks.task == task
                select weeks).FirstOrDefault();
            if (weekRow == null)
            {
                throw new Exception("Exception in saving time!!!");
            }
            weekRow.time = time;
            try
            {
                context.SubmitChanges();
            }
            catch (Exception)
            {
                throw new Exception("Can't save time exception!!!");
            }
        }
    }
}